跳到主要内容

VSCode 编写插件 - 语法高亮

语法高亮的官方文档参考 TextMate

这里主要介绍的是 声明式 的语法扩展

配置环境

首先配置环境

# 安装脚手架
npm install -g yo generator-code
# 到自己工程目录下
yo code

选择 New Language Support

然后按照提示 Next

# Enter the URL (http, https) or the file path of the tmLanguage grammar or
press ENTER to start with a new grammar.
? URL or file to import, or none for new: (如果有自制语言的语法文件就输入URL)

? What's the name of your extension? example-language-support (输入插件名)

? What's the identifier of your extension? example (一般输入语言的名字)

? What's the description of your extension? An example description (对于语言的描述)

# Enter the id of the language. The id is an identifier and is single,
lower-case name such as 'php', 'javascript'
? Language id: example

# Enter the name of the language. The name will be shown in the VS Code editor
mode selector.
? Language name: example

# Enter the file extensions of the language. Use commas to separate
multiple entries (e.g. .ruby, .rb)
? File extensions: .eg (输入语言文件的后缀)

# Enter the root scope name of the grammar (e.g. source.ruby)
? Scope names: source.example (根据自己的情况填,一般来说是 “source.(语言名)” )

各文件介绍

生成好初始化库之后点开 package.json 文件,这个文件主要用来配置一些项目上的东西

{
"name": "xxxx", // 项目名
"displayName": "xxxx-support",
"description": "xxxx 的语法高亮",
"version": "0.0.1",
"engines": {
"vscode": "^1.63.0"
},
"categories": [
"Programming Languages" // 语言分类
],
"contributes": {
"languages": [{
"id": "mw",
"aliases": ["xxxxxx", "mw"],
"extensions": [".case.json"], // 后缀名
"configuration": "./language-configuration.json"
}],
"grammars": [{
"language": "mw",
"scopeName": "source.case.json",
"path": "./syntaxes/mw.tmLanguage.json"
}]
}
}

然后 language-configuration.json 文件则是用来配置快捷键(快速注释),这里标识了哪些是注释,哪些是括号

{
"comments": {
// 单行注释(如果不支持则可以删掉它)
"lineComment": "//",
// 多行注释
"blockComment": [ "/*", "*/" ]
},
// symbols used as brackets
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
// symbols that can be used to surround a selection
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
]
}

然后开始编写语法高亮文件,打开在 syntaxes 文件夹里的 tmLanguage.json 文件

{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "xxxxxx",
"patterns": [
{
"include": "#keywords"
},
{
"include": "#strings"
}
],
"repository": { // 这里的 repository 表示本地库,要引用本地库需要加 #
"keywords": {
"patterns": [{
"name": "keyword.control.mw",
"match": "\\b(if|while|for|return)\\b"
}]
},
"strings": {
"name": "string.quoted.double.mw",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.mw",
"match": "\\\\."
}
]
}
},
"scopeName": "source.case.json" // 语言文件的唯一名称,例如这里就是 .case.json 后缀的文件
}

基本概念

patterns:由语法分析规则(parse rules)所组成的数组。上面的语法分析规则指的是 keywords 还有 strings 两项。

name:指的是与规则相匹配的代码的种类。主要是用来表示这个规则底下的代码到底是什么颜色的,比如上面例子里如果一串代码满足 strings 规则,那么在 VS Code 里它就会被标记成当前主题下 strings 的颜色。这里的 name 是不能随便填的,具体有哪些选项请参考这个 网页

match:正则表达式,用来指示标记引擎哪些代码改被归为一类。上面的例子里如果一串代码满足 "\\b(if|while|for|return)\\b",就会被归纳为 keyword.control。

include:用来引用别的语言或是递归式地引用自己本身的语法。假设我们想要给所有的数据类型标记高亮,那么就可以用以下 pattern 来表示。这里的 array-type,pair-type 还有 base-type 所指的就是同一个文件中其他的规则。

"types": {
"patterns": [
{
"include": "#array-type" // 注意这里要加 # 表示是当前文件定义的
},
{
"include": "#pair-type"
},
{
"include": "#base-types"
}
]
}

begin, end:这两个关键词都是正则表达式,和 match 不能同时使用。begin 用来标记一个多行字符串的开头应该满足的条件, end 则用来标记结尾满足的条件。

{
"patterns": [
{
"name": "keyword.control",
"match": "\b(if|while|for|return)\b"
}
]
}

示例中,patterns 用于定义规则集合, match 属性定于用于匹配 token 的正则,name 属性声明该 token 的分类(scope),TextMate 分词过程遇到匹配 match 正则的内容时,会将其看作单独 token 处理并分类为 name 声明的 keyword.control 类型。

上述示例会将 if/while/for/return 关键词识别为 keyword.control 类型,但无法识别其它关键字

在 TextMate 语境中,scope 是一种 . 分割的层级结构,例如 keyword 与 keyword.control 形成父子层级,这种层级结构在样式处理逻辑中能实现一种类似 css 选择器的匹配。

引用不同的语言

可以使用 include 来引用不同的语言到当前文件中

1、要引用另一种语言,需要使用该语言的作用域名称

 {  
"begin" : "<\?(php|=)?",
"end" : "\?>",
"patterns" : [
{
"include" : "source.php"
}
]
}

2、如果需要引用当前语法库则使用 #

 patterns = (
{ begin = '"'; end = '"'; patterns = (
{ include = "#escaped-char"; },
{ include = "#variable"; }
);
},

); // end of patterns
repository = {
escaped-char = { match = '\\.'; };
variable = { match = '\$[a-zA-Z0-9_]+'; };
};

References

你不知道的 VSCode 代码高亮原理 【VSCode插件开发】让VSCode高亮显示“三连是好活儿” 从零开始为你的自制编程语言写一个专属VSCode插件